package cn.xzc.job.cocurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 异步回调通知
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */
public class ListenableFutureTask<T> extends FutureTask<T> implements ListenableFuture<T> {

    /**
     * 存储所有成功和失败回调通知
     */
    private final ListenableFutureCallbackRegistry<T> callbacks = new ListenableFutureCallbackRegistry<>();


    /**
     * Create a new {@code ListenableFutureTask} that will, upon running, execute the given {@link Callable}.
     *
     * @param callable the callable task 任务
     */
    public ListenableFutureTask(Callable<T> callable) {
        super(callable);
    }

    /**
     * Create a {@code ListenableFutureTask} that will, upon running,
     * execute the given {@link Runnable}, and arrange that {@link #get()}
     * will return the given result on successful completion.
     *
     * @param runnable the runnable task 任务
     * @param result   the result to return on successful completion
     */
    public ListenableFutureTask(Runnable runnable, T result) {
        super(runnable, result);
    }


    /**
     * 注册所有通知【成功、失败】
     *
     * @param callback the callback to register 通知
     */
    @Override
    public void addCallback(ListenableFutureCallback<? super T> callback) {
        this.callbacks.addCallback(callback);
    }

    /**
     * 注册所有通知【成功、失败】
     *
     * @param successCallback the success callback 成功回调
     * @param failureCallback the failure callback 失败回调
     */
    @Override
    public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
        this.callbacks.addSuccessCallback(successCallback);
        this.callbacks.addFailureCallback(failureCallback);
    }

    @Override
    public CompletableFuture<T> completable() {
        CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
        this.callbacks.addSuccessCallback(completable::complete);
        this.callbacks.addFailureCallback(completable::completeExceptionally);
        return completable;
    }


    @Override
    protected void done() {
        Throwable cause;
        try {
            T result = get();
            this.callbacks.success(result);
            return;
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            return;
        } catch (ExecutionException ex) {
            cause = ex.getCause();
            if (cause == null) {
                cause = ex;
            }
        } catch (Throwable ex) {
            cause = ex;
        }
        this.callbacks.failure(cause);
    }

}